﻿using Microsoft.Extensions.Primitives;

using System.Reflection;

namespace Acl.Messages.Impl;

/// <summary>
/// 分布式消息处理管理器
/// </summary>
class DistributionMessageHandleManagement : IAsyncDisposable, IDistributionMessageHandleManagement
{
    static readonly MethodInfo _addNotificationHandlerMethod = typeof(DistributionMessageHandleManagement).GetMethods(BindingFlags.Public | BindingFlags.Instance)
        .Where(t => t.Name == $"{nameof(AddNotificationHandler)}")
        .Where(t => t.IsGenericMethod)
        .FirstOrDefault()!;

    static readonly MethodInfo _addRequestHandlerMethod = typeof(DistributionMessageHandleManagement).GetMethods(BindingFlags.Public | BindingFlags.Instance)
        .Where(t => t.Name == $"{nameof(AddRequestHandler)}")
        .Where(t => t.IsGenericMethod)
        .FirstOrDefault()!;

    private readonly IDistributionMessageSubscriber _subscriber;
    private readonly List<IDistributionMessageHandlerBase> _handlers = new();


    /// <summary>
    /// 处理器数量
    /// </summary>
    public int HandlerCount => _handlers.Count;

    /// <summary>
    /// 
    /// </summary>
    /// <param name="subscriber"></param>
    /// <param name="serviceProvider"></param>
    public DistributionMessageHandleManagement(IDistributionMessageSubscriber subscriber, IServiceProvider? serviceProvider = default) : this(subscriber, serviceProvider?.GetServices<IDistributionMessageHandlerBase>())
    {
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="subscriber"></param>
    /// <param name="handlers"></param>
    private DistributionMessageHandleManagement(IDistributionMessageSubscriber subscriber, IEnumerable<IDistributionMessageHandlerBase>? handlers = default)
    {
        _subscriber = subscriber;
        BatchAddHandlers(handlers).GetAwaiter().GetResult();
    }

    private async Task BatchAddHandlers(IEnumerable<IDistributionMessageHandlerBase>? handlers)
    {
        if (handlers == null) return;

        foreach (var handler in handlers)
            await AddHandler(handler).ConfigureAwait(false);
    }


    /// <summary>
    /// 
    /// </summary>
    /// <param name="handler"></param>
    /// <returns></returns>
    public Task AddHandler(IDistributionMessageHandlerBase handler)
    {
        var type = handler.GetType().GetTypeInfo();
        var interfaceType = type.ImplementedInterfaces.FirstOrDefault(t => typeof(IDistributionMessageHandlerBase).IsAssignableFrom(t) && t.IsGenericType);

        if (interfaceType != null)
        {
            var openInterfaceType = interfaceType.GetGenericTypeDefinition();

            if (openInterfaceType == typeof(IDistributionNotificationHandler<>))
            {
                var result = _addNotificationHandlerMethod.MakeGenericMethod(interfaceType.GetGenericArguments()).Invoke(this, new object[] { handler });

                return (Task)result!;
            }
            else if (openInterfaceType == typeof(IDistributionRequestHandler<,>))
            {
                var result = _addRequestHandlerMethod.MakeGenericMethod(interfaceType.GetGenericArguments()).Invoke(this, new object[] { handler });

                return (Task)result!;
            }
        }

        throw new ArgumentException($"无效的消息处理器类型:{type.FullName}");
    }

    /// <summary>
    /// 注册处理器
    /// </summary>
    /// <typeparam name="TData"></typeparam>
    /// <param name="handler"></param>
    /// <returns></returns>
    public Task AddNotificationHandler<TData>(IDistributionNotificationHandler<TData> handler)
    {
        if (handler is null) return Task.CompletedTask;
        var item = _handlers.FirstOrDefault(x => x == handler);
        if (item != null) return Task.CompletedTask;

        ArgumentNullException.ThrowIfNull(handler.Options);
        ArgumentException.ThrowIfNullOrEmpty(handler.Options!.Value.Topic);

        _handlers.Add(handler);

        return _subscriber.Subscribe<TData>(handler.Options!.Value.Topic, async msg =>
        {
            await handler.Handle(msg);
        });
    }

    record struct DistributionRequest<TData>(string Topic, IDictionary<string, StringValues>? Headers, TData? Data) : IDistributionRequest<TData>;


    /// <summary>
    /// 注册处理器
    /// </summary>
    /// <typeparam name="TData"></typeparam>
    /// <param name="handler"></param>
    /// <typeparam name="TResponse"></typeparam>
    /// <returns></returns>
    public Task AddRequestHandler<TData, TResponse>(IDistributionRequestHandler<TData, TResponse> handler)
    {
        if (handler is null) return Task.CompletedTask;
        var item = _handlers.FirstOrDefault(x => x == handler);
        if (item != null) return Task.CompletedTask;

        ArgumentNullException.ThrowIfNull(handler.Options);
        ArgumentNullException.ThrowIfNullOrEmpty(handler.Options!.Value.Topic);

        _handlers.Add(handler);

        return _subscriber.Subscribe<TData>(handler.Options!.Value.Topic, async msg =>
        {
            var response = await handler.Handle(new DistributionRequest<TData>(msg.Topic, msg.Headers, msg.Data));

            await msg.ReplyAsync<TResponse>(response).ConfigureAwait(false);
        });

    }


    /// <summary>
    /// 移除处理器
    /// </summary>
    /// <param name="handler"></param>
    /// <returns></returns>
    public async Task RemoveHandler(IDistributionMessageHandlerBase handler)
    {
        if (handler is null) return;
        _handlers.Remove(handler);

        if (handler is IDisposable disposable) disposable.Dispose();
        if (handler is IAsyncDisposable disposable2) await disposable2.DisposeAsync().ConfigureAwait(false);
    }

    /// <summary>
    /// 清空所有处理器
    /// </summary>
    private async Task ClearHandlers()
    {
        foreach (var handler in _handlers)
        {
            if (handler is null) return;
            if (handler is IDisposable disposable) disposable.Dispose();
            if (handler is IAsyncDisposable disposable2) await disposable2.DisposeAsync().ConfigureAwait(false);
        }

        _handlers.Clear();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public async ValueTask DisposeAsync() => await ClearHandlers().ConfigureAwait(false);
}


